#include <unistd.h>
#include <cerrno>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <sys/poll.h>

#include "dataDevice.h"
#include "readConfig.h"

using namespace std;

const char ConfigFile[] = "config";
char buff[65535];
string mode;

union address
{
  struct sockaddr_in addrV4;
	struct sockaddr_in6 addrV6;
};

address saddr;

int connect2Server(pollfd *pfds)
{
  int fd;
	int res;

	//Create socket
	if(mode == "ipv4")
	  fd = socket(AF_INET, SOCK_STREAM, 0);
	else if(mode == "ipv6")
	  fd = socket(AF_INET6, SOCK_STREAM, 0);

	if(-1 == fd)
  {
    cerr<<"Create socket error: "<<strerror(errno)<<endl;
		goto error;
  }
  //cout<<"Connected socket fd: "<<fd<<endl;
    
	//Connect to server
	char ip2Dot[20];

	cout<<endl;
	if(mode == "ipv4")
	{
	  inet_ntop(AF_INET, (void *)&saddr.addrV4.sin_addr, ip2Dot, sizeof(struct sockaddr_in));
    cout<<"Connecting to "<<ip2Dot<<", Port: "<<ntohs(saddr.addrV4.sin_port)<<endl;
	  res = connect(fd, (struct sockaddr*)&saddr, sizeof(struct sockaddr_in));
	}
	else if(mode == "ipv6")
	{
	  inet_ntop(AF_INET6, (void *)&saddr.addrV6.sin6_addr, ip2Dot, sizeof(struct sockaddr_in6));
    cout<<"Connecting to "<<ip2Dot<<", Port: "<<ntohs(saddr.addrV6.sin6_port)<<endl;
	  res = connect(fd, (struct sockaddr*)&saddr, sizeof(struct sockaddr_in6));
	}

	if(-1 == res)
	{
	  cout<<"Connect failed"<<endl;
	  close(fd);
		goto error;
	}
	cout<<"Connect OK."<<endl;

  //Set poll
	pfds->fd = fd;
  cout<<"connect: "<<res<<endl;
	error:
	return (res == 0);
}

int main(int argc, char **argv)
{
  //daemon(0, 0);
  int res;

  //----------------------------------------------------------------------------------
  //Get conf
  /*get configure info*/
  Config configSettings(ConfigFile);

  /* get device configure */
  string deviceName;
  deviceName = configSettings.Read("dataDeviceName", deviceName);
  cout<<"Serial device"<<endl;

  /* create device */
  dataDevice *device;
  if(deviceName == "serial")
  {
    string dataDeviceSerial;
    dataDeviceSerial = configSettings.Read("dataDeviceSerial", dataDeviceSerial);
    device = new serialDataDevice(dataDeviceSerial.c_str());
  }

  /* open device */
  device->open();

	/* Get network mode: ipv4 or ipv6*/
	mode = configSettings.Read("mode", mode);
	cout<<"Network mode: "<<mode<<endl;

  /*build data server address*/
  if(mode == "ipv6")
	{
    string dataServerAddressV6;
    dataServerAddressV6 = configSettings.Read("dataServerAddressV6", dataServerAddressV6);
    int dataServerPortV6 = configSettings.Read("dataServerPortV6", 0);
    //cout<<"Data server address: "<<dataServerAddressV6<<" ,Port: "<<dataServerPortV6<<endl;

    struct sockaddr_in6 *addr = (struct sockaddr_in6*)&saddr.addrV6;
    bzero(addr, sizeof(struct sockaddr_in6));
    addr->sin6_family = AF_INET6;
    addr->sin6_port = htons(dataServerPortV6);
    inet_pton(AF_INET6, dataServerAddressV6.c_str(), &addr->sin6_addr);
  }
	else if(mode == "ipv4")
	{
    string dataServerAddressV4;
    dataServerAddressV4 = configSettings.Read("dataServerAddressV4", dataServerAddressV4);
    int dataServerPortV4 = configSettings.Read("dataServerPortV4", 0);
    //cout<<"Data server address: "<<dataServerAddressV4<<" ,Port: "<<dataServerPortV4<<endl;

    struct sockaddr_in *addr = (struct sockaddr_in*)&saddr.addrV4;
		bzero(addr, sizeof(struct sockaddr_in));
		addr->sin_family = AF_INET;
		addr->sin_port = htons(dataServerPortV4);
		inet_pton(AF_INET, dataServerAddressV4.c_str(), &addr->sin_addr);
	}
	else
	{
    cout<<"Network mode : "<<mode<<" is error."<<endl;
		exit(1);
	}

	bool connected = false;

  struct pollfd fds[2];

  //serial port file descriptor
  fds[0].fd = device->getFd();
  fds[0].events = POLLIN;

	//TCP socket file descriptor
	fds[1].events = POLLIN;
	connected = connect2Server(&fds[1]);

  while(true)
  {
	  if(!connected)
		{
		  cout<<"Reconnecting..."<<endl;
			sleep(1);
		  connected = connect2Server(&fds[1]);
			continue;
		}
	  //cout<<"fds[1]: "<<fds[1].fd<<endl;
    res = poll(fds, 2, -1);
    if(res < 0)
    {
      cout<<"Poll error, exit...."<<endl;
      exit(1);
    }
    else if(res == 0)
    {
      cout<<"Poll timeout"<<endl;
      continue;
    }

    bzero(buff, sizeof(buff));

    /* data from searial */
    if(fds[0].revents & POLLIN)
    {
		  cout<<"\n------------ Serial Read Start ------------"<<endl;
      int readn = device->read(buff, sizeof(buff));

      if(readn < 0)
      {
        if(errno == EINTR)
				  continue;
        cout<<"device error"<<endl;
        exit(1);
      }
      else if(readn > 0)
      {
        res = send(fds[1].fd, buff, readn, 0);
				cout<<"Send "<<res<<" bytes to data server"<<endl;
      }
		  cout<<"------------ Serial Read End ------------"<<endl;
    }//end if

    //Date from TCP server
		if(fds[1].revents & POLLIN)
		{
		  cout<<"------------ Socket Read Start ------------"<<endl;
		  int readn = recv(fds[1].fd, buff, sizeof(buff), 0);
			if(readn > 0)
			{
			  cout<<"Read "<<readn<<" bytes from TCP server"<<endl;
        device->write(buff, readn);
			}
			else if(readn == 0)
			{
        cout<<"Disconnect from server"<<endl;
				close(fds[1].fd);
				connected = false;
			}
      else
			{
        if(errno == EINTR)
				  continue;
			  cout<<"Read from TCP server error, exit."<<endl;
				close(fds[1].fd);
				connected = false;
			}
		  cout<<"------------ Socket Read End ------------"<<endl;
		}//end if

		cout<<endl;
  }//end while
  
  return 0;
}//end main
